```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>算法精解：无重复字符的最长子串</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            background-color: #f8fafc;
            color: #1e293b;
        }
        .hero-gradient {
            background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
        }
        .code-block {
            background-color: #1e1e2e;
            border-left: 4px solid #8b5cf6;
        }
        .card-hover {
            transition: all 0.3s ease;
        }
        .card-hover:hover {
            transform: translateY(-5px);
            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
        }
        .drop-cap::first-letter {
            font-family: 'Noto Serif SC', serif;
            font-weight: 700;
            float: left;
            font-size: 3.5rem;
            line-height: 3rem;
            padding-right: 0.5rem;
            margin-top: 0.3rem;
            color: #6366f1;
        }
    </style>
</head>
<body class="antialiased">
    <!-- Hero Section -->
    <section class="hero-gradient text-white py-20 px-4 md:py-32">
        <div class="container mx-auto max-w-6xl">
            <div class="flex flex-col md:flex-row items-center">
                <div class="md:w-1/2 mb-10 md:mb-0">
                    <h1 class="text-4xl md:text-5xl font-bold mb-4 leading-tight">无重复字符的最长子串</h1>
                    <p class="text-xl md:text-2xl font-light mb-6 opacity-90">滑动窗口与哈希表的完美结合</p>
                    <div class="flex items-center">
                        <div class="bg-white text-indigo-600 px-4 py-2 rounded-full font-medium inline-flex items-center">
                            <i class="fas fa-tag mr-2"></i>
                            <span>中等难度</span>
                        </div>
                        <div class="ml-4 bg-white text-indigo-600 px-4 py-2 rounded-full font-medium inline-flex items-center">
                            <i class="fas fa-clock mr-2"></i>
                            <span>时间复杂度: O(n)</span>
                        </div>
                    </div>
                </div>
                <div class="md:w-1/2">
                    <div class="bg-white bg-opacity-10 p-6 rounded-xl backdrop-blur-sm border border-white border-opacity-20">
                        <div class="flex items-center mb-4">
                            <div class="w-3 h-3 bg-red-500 rounded-full mr-2"></div>
                            <div class="w-3 h-3 bg-yellow-500 rounded-full mr-2"></div>
                            <div class="w-3 h-3 bg-green-500 rounded-full"></div>
                        </div>
                        <div class="code-block p-4 rounded-lg overflow-x-auto">
                            <pre class="text-gray-200 font-mono text-sm"><code>输入: "abcabcbb"
输出: 3
解释: 最长无重复子串是 "abc"，长度是 3</code></pre>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <div class="container mx-auto max-w-6xl px-4 py-12">
        <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
            <!-- Left Column - Problem Explanation -->
            <div class="lg:col-span-2">
                <article class="prose prose-indigo max-w-none">
                    <h2 class="text-3xl font-bold text-gray-800 mb-6">题目解析</h2>
                    <div class="drop-cap mb-8">
                        <p>给定一个字符串，找出其中不含有重复字符的<strong>最长子串</strong>的长度。这个问题看似简单，却蕴含着经典的算法思想，也是面试中的高频题目。通过这道题，我们可以深入理解滑动窗口这一重要技术，并掌握如何使用哈希表来优化算法性能。</p>
                    </div>

                    <div class="bg-blue-50 border-l-4 border-blue-500 p-4 mb-8 rounded-r">
                        <div class="flex">
                            <div class="flex-shrink-0">
                                <i class="fas fa-lightbulb text-blue-500 text-xl"></i>
                            </div>
                            <div class="ml-3">
                                <h3 class="text-lg font-medium text-blue-800">核心考点</h3>
                                <div class="mt-2 text-blue-700">
                                    <p>滑动窗口、哈希表应用、字符串处理</p>
                                </div>
                            </div>
                        </div>
                    </div>

                    <h3 class="text-2xl font-semibold text-gray-800 mt-10 mb-4">解题思路</h3>
                    <p class="mb-4">我们使用<strong>滑动窗口</strong>技术来维护一个不含重复字符的子串。窗口的两个边界分别由左指针和右指针表示。右指针不断向右移动扩展窗口，当遇到重复字符时，左指针移动到重复字符的下一个位置。</p>

                    <div class="grid md:grid-cols-2 gap-6 my-8">
                        <div class="bg-white p-6 rounded-xl shadow-sm border border-gray-100 card-hover">
                            <div class="flex items-center mb-4">
                                <div class="bg-indigo-100 p-2 rounded-lg text-indigo-600 mr-4">
                                    <i class="fas fa-window-maximize text-xl"></i>
                                </div>
                                <h4 class="text-lg font-semibold text-gray-800">滑动窗口</h4>
                            </div>
                            <p class="text-gray-600">动态调整的子串范围，左边界遇到重复字符时收缩，右边界不断扩展。</p>
                        </div>
                        <div class="bg-white p-6 rounded-xl shadow-sm border border-gray-100 card-hover">
                            <div class="flex items-center mb-4">
                                <div class="bg-purple-100 p-2 rounded-lg text-purple-600 mr-4">
                                    <i class="fas fa-key text-xl"></i>
                                </div>
                                <h4 class="text-lg font-semibold text-gray-800">哈希表</h4>
                            </div>
                            <p class="text-gray-600">记录每个字符最后出现的位置，快速判断是否重复并定位。</p>
                        </div>
                    </div>

                    <div class="my-8">
                        <div class="mermaid">
                            graph TD
                                A[开始] --> B[初始化哈希表和指针]
                                B --> C[右指针遍历字符串]
                                C --> D{字符是否在哈希表中且≥左指针?}
                                D -->|是| E[移动左指针到重复字符+1]
                                D -->|否| F[更新最大长度]
                                E --> G[更新字符位置]
                                F --> G
                                G --> C
                                C --> H[遍历结束]
                                H --> I[返回最大长度]
                        </div>
                    </div>

                    <h3 class="text-2xl font-semibold text-gray-800 mt-10 mb-4">代码实现</h3>
                    <div class="code-block p-4 rounded-lg mb-6 overflow-x-auto">
                        <pre class="text-gray-200 font-mono text-sm"><code>public int lengthOfLongestSubstring(String s) {
    // 创建哈希表，记录每个字符最后出现的位置
    Map&lt;Character, Integer&gt; charIndex = new HashMap&lt;&gt;();
    
    // 最长无重复子串的长度
    int maxLength = 0;
    
    // 滑动窗口的左边界
    int left = 0;
    
    // 遍历字符串，右指针不断向右移动
    for (int right = 0; right &lt; s.length(); right++) {
        char currentChar = s.charAt(right);
        
        // 如果当前字符已经在滑动窗口中出现过，更新左边界
        if (charIndex.containsKey(currentChar) && charIndex.get(currentChar) &gt;= left) {
            // 左边界移动到重复字符的下一个位置
            left = charIndex.get(currentChar) + 1;
        } else {
            // 如果没有重复，计算当前无重复子串的长度，并更新最大长度
            maxLength = Math.max(maxLength, right - left + 1);
        }
        
        // 更新当前字符的位置
        charIndex.put(currentChar, right);
    }
    
    // 返回最长无重复子串的长度
    return maxLength;
}</code></pre>
                    </div>

                    <div class="bg-yellow-50 border-l-4 border-yellow-500 p-4 mb-8 rounded-r">
                        <div class="flex">
                            <div class="flex-shrink-0">
                                <i class="fas fa-exclamation-circle text-yellow-500 text-xl"></i>
                            </div>
                            <div class="ml-3">
                                <h3 class="text-lg font-medium text-yellow-800">边界情况</h3>
                                <div class="mt-2 text-yellow-700">
                                    <ul class="list-disc pl-5 space-y-1">
                                        <li>空字符串应返回0</li>
                                        <li>全相同字符的字符串应返回1</li>
                                        <li>无重复字符的字符串返回字符串长度</li>
                                    </ul>
                                </div>
                            </div>
                        </div>
                    </div>
                </article>
            </div>

            <!-- Right Column - Key Concepts -->
            <div class="lg:col-span-1">
                <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 sticky top-6">
                    <h3 class="text-xl font-bold text-gray-800 mb-4 flex items-center">
                        <i class="fas fa-key mr-2 text-indigo-500"></i>
                        核心概念
                    </h3>
                    
                    <div class="space-y-4">
                        <div class="p-4 bg-indigo-50 rounded-lg">
                            <h4 class="font-semibold text-indigo-700 mb-2">滑动窗口</h4>
                            <p class="text-sm text-gray-600">一种通过动态调整窗口边界来解决问题的技术，常用于数组/字符串的子区间问题。</p>
                        </div>
                        
                        <div class="p-4 bg-purple-50 rounded-lg">
                            <h4 class="font-semibold text-purple-700 mb-2">哈希表优化</h4>
                            <p class="text-sm text-gray-600">利用哈希表O(1)的查找特性，快速判断字符是否重复并记录位置。</p>
                        </div>
                        
                        <div class="p-4 bg-blue-50 rounded-lg">
                            <h4 class="font-semibold text-blue-700 mb-2">时间复杂度</h4>
                            <p class="text-sm text-gray-600">该解法只需遍历一次字符串，时间复杂度为O(n)，n为字符串长度。</p>
                        </div>
                    </div>
                    
                    <h3 class="text-xl font-bold text-gray-800 mt-8 mb-4 flex items-center">
                        <i class="fas fa-lightbulb mr-2 text-yellow-500"></i>
                        类似题目
                    </h3>
                    
                    <ul class="space-y-3">
                        <li class="flex items-start">
                            <span class="bg-gray-100 text-gray-600 rounded-full w-6 h-6 flex items-center justify-center text-xs mr-3 flex-shrink-0">1</span>
                            <span class="text-gray-700">至多包含两个不同字符的最长子串</span>
                        </li>
                        <li class="flex items-start">
                            <span class="bg-gray-100 text-gray-600 rounded-full w-6 h-6 flex items-center justify-center text-xs mr-3 flex-shrink-0">2</span>
                            <span class="text-gray-700">至多包含K个不同字符的最长子串</span>
                        </li>
                        <li class="flex items-start">
                            <span class="bg-gray-100 text-gray-600 rounded-full w-6 h-6 flex items-center justify-center text-xs mr-3 flex-shrink-0">3</span>
                            <span class="text-gray-700">最小覆盖子串</span>
                        </li>
                    </ul>
                </div>
            </div>
        </div>

        <!-- Visualization Section -->
        <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-8 mt-12">
            <h2 class="text-3xl font-bold text-gray-800 mb-6">算法可视化</h2>
            <p class="text-gray-600 mb-8">通过动画演示理解滑动窗口如何动态调整边界来寻找最长无重复字符子串。</p>
            
            <div class="flex flex-col items-center">
                <div class="relative w-full bg-gray-100 rounded-lg p-6 mb-8" style="height: 300px;">
                    <div class="absolute top-1/2 left-0 right-0 h-16 bg-indigo-100 rounded-md" id="window"></div>
                    <div class="absolute top-1/2 left-0 w-full flex justify-center" id="string-display"></div>
                    <div class="absolute top-1/4 left-0 w-full text-center">
                        <div class="inline-block bg-white px-4 py-2 rounded-lg shadow-sm">
                            <span class="font-mono" id="current-substring"></span>
                        </div>
                    </div>
                    <div class="absolute bottom-4 left-0 w-full text-center">
                        <button id="start-btn" class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition">
                            <i class="fas fa-play mr-2"></i>开始演示
                        </button>
                    </div>
                </div>
                
                <div class="w-full overflow-x-auto">
                    <table class="min-w-full divide-y divide-gray-200">
                        <thead class="bg-gray-50">
                            <tr>
                                <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">步骤</th>
                                <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">右指针</th>
                                <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">字符</th>
                                <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">哈希表更新</th>
                                <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">左指针</th>
                                <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">最大长度</th>
                            </tr>
                        </thead>
                        <tbody class="bg-white divide-y divide-gray-200" id="steps-table">
                            <!-- 步骤将通过JavaScript动态生成 -->
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <script>
        mermaid.initialize({ 
            startOnLoad: true,
            theme: 'default',
            flowchart: { 
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
        
        // 简单的演示动画
        document.addEventListener('DOMContentLoaded', function() {
            const sampleString = "abcabcbb";
            const stringDisplay = document.getElementById('string-display');
            const windowElement = document.getElementById('window');
            const currentSubstring = document.getElementById('current-substring');
            const stepsTable = document.getElementById('steps-table');
            const startBtn = document.getElementById('start-btn');
            
            // 初始化字符串显示
            stringDisplay.innerHTML = '';
            for (let i = 0; i < sampleString.length; i++) {
                const charSpan = document.createElement('span');
                charSpan.className = 'inline-block w-10 h-10 flex items-center justify-center text-lg font-mono font-bold bg-white rounded-full shadow-sm mx-1';
                charSpan.textContent = sampleString[i];
                charSpan.id = `char-${i}`;
                stringDisplay.appendChild(charSpan);
            }
            
            // 初始化窗口
            windowElement.style.width = '80px';
            windowElement.style.left = '0';
            windowElement.style.transform = 'translateY(-50%)';
            
            // 模拟算法步骤
            const steps = [
                { right: 0, char: 'a', map: {'a': 0}, left: 0, maxLen: 1, substring: 'a' },
                { right: 1, char: 'b', map: {'a': 0, 'b': 1}, left: 0, maxLen: 2, substring: 'ab' },
                { right: 2, char: 'c', map: {'a': 0, 'b': 1, 'c': 2}, left: 0, maxLen: 3, substring: 'abc' },
                { right: 3, char: 'a', map: {'a': 3, 'b': 1, 'c': 2}, left: 1, maxLen: 3, substring: 'bca' },
                { right: 4, char: 'b', map: {'a': 3, 'b': 4, 'c': 2}, left: 2, maxLen: 3, substring: 'cab' },
                { right: 5, char: 'c', map: {'a': 3, 'b': 4, 'c': 5}, left: 3, maxLen: 3, substring: 'abc' },
                { right: 6, char: 'b', map: {'a': 3, 'b': 6, 'c': 5}, left: 5, maxLen: 3, substring: 'cb' },
                { right: 7, char: 'b', map: {'a': 3, 'b': 7, 'c': 5}, left: 7, maxLen: 3, substring: 'b' }
            ];
            
            // 填充步骤表格
            steps.forEach((step, index) => {
                const row = document.createElement('tr');
                row.className = index % 2 === 0 ? 'bg-white' : 'bg-gray-50';
                
                const cells = [
                    index + 1,
                    step.right,
                    step.char,
                    JSON.stringify(step.map).replace(/"/g, ''),
                    step.left,
                    step.maxLen
                ];
                
                cells.forEach(cellData => {
                    const cell = document.createElement('td');
                    cell.className = 'px-6 py-4 whitespace-nowrap text-sm text-gray-500';
                    cell.textContent = cellData;
                    row.appendChild(cell);
                });
                
                stepsTable.appendChild(row);
            });
            
            // 动画演示
            let currentStep = 0;
            startBtn.addEventListener('click', function() {
                startBtn.disabled = true;
                startBtn.innerHTML = '<i class="fas fa-sync-alt mr-2"></i>重新开始';
                
                const animateStep = () => {
                    if (currentStep >= steps.length) {
                        currentStep = 0;
                        startBtn.disabled = false;
                        return;
                    }
                    
                    const step = steps[currentStep];
                    
                    // 更新窗口位置和宽度
                    const windowWidth = step.substring.length * 42;
                    const windowLeft = step.left * 42 + (step.left * 8);
                    windowElement.style.width = `${windowWidth}px`;
                    windowElement.style.left = `${windowLeft}px`;
                    
                    // 高亮当前字符
                    document.querySelectorAll('.bg-yellow-200').forEach(el => {
                        el.classList.remove('bg-yellow-200', 'text-yellow-800');
                    });
                    document.getElementById(`char-${step.right}`).classList.add('bg-yellow-200', 'text-yellow-800');
                    
                    // 更新当前子串显示
                    currentSubstring.textContent = step.substring;
                    
                    // 高亮表格当前行
                    document.querySelectorAll('tr.bg-indigo-50').forEach(el => {
                        el.classList.remove('bg-indigo-50');
                    });
                    stepsTable.children[currentStep].classList.add('bg-indigo-50');
                    
                    currentStep++;
                    setTimeout(animateStep, 1500);
                };
                
                animateStep();
            });
        });
    </script>
</body>
</html>
```